﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.IO;
using Update.ServiceReference1;
using System.Data;
using System.Threading;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Diagnostics;
using System.ServiceModel;
using System.Text;

namespace Update
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow
    {
        readonly string _parmName = string.Empty;
        public MainWindow(string parmName)
        {
            InitializeComponent();
           
            if (!string.IsNullOrEmpty(parmName))
            {
                _parmName = parmName;
            }

            UpdateList = new ObservableCollection<UpdateEntity>();
            this.listView1.ItemsSource = this.UpdateList;
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }


        private void KillProcess()
        {
            if (_parmName != string.Empty)
            {
                string name = Path.GetFileNameWithoutExtension(_parmName);
                Process myProcesses = Process.GetProcesses().FirstOrDefault(o => o.ProcessName == name);
                if (myProcesses != null)
                {
                    myProcesses.Kill();
                }
            }
        }
        private void StartProcess()
        {
            if (_parmName != string.Empty)
            {
                Process.Start(_parmName);
            }
        }

        public ObservableCollection<UpdateEntity> UpdateList
        { get; set; }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {

            ThreadPool.QueueUserWorkItem(o =>
                                             {
                                                 BasicHttpBinding bingding = new BasicHttpBinding(BasicHttpSecurityMode.None)
                                                                                 {
                                                                                     MaxReceivedMessageSize = 1024*1024,
                                                                                     ReaderQuotas = {MaxArrayLength = 1024*1024}
                                                                                 };
                                                 EndpointAddress endpoint= new EndpointAddress(@"http://192.168.23.240:8888/DataCenter");

                                                 DataAccessCenterClient client = new DataAccessCenterClient(bingding, endpoint);
                                                 try
                                                 {
                                                     bool isDonw = (bool)client.GetObject(new ParamesEntity { ClassName = "DownLoadFile", MethodName = "IsDown", Values = new object[] { string.Empty, _parmName } });
                                                     if (isDonw)
                                                     {
                                                         KillProcess();
                                                         Thread.Sleep(1000);
                                                         DownDirectory(client);
                                                         DownFile(client);

                                                     }
                                                     else
                                                     {
                                                         MessageBox.Show("目前已经是最新版本，不需要更新。");
                                                         this.Dispatcher.Invoke(new Action(this.Close));
                                                     }
                                                 }
                                                 catch (Exception ex)
                                                 {
                                                     MessageBox.Show(ex.Message);
                                                     this.Dispatcher.Invoke(new Action(this.Close));
                                                 }
                                                 finally
                                                 {
                                                     client.Close();
                                                 }
                                             });

        }

        /// <summary>
        /// 下载目录
        /// </summary>
        /// <param name="client"></param>
        private void DownDirectory(DataAccessCenterClient client)
        {
            DataTable dt = client.GetTable(new ParamesEntity { ClassName = "DownLoadFile", MethodName = "DownLoadFileNames", Values = null });
            this.Dispatcher.Invoke(new Action(() =>
            {
                foreach (DataRow row in dt.Rows)
                {
                    UpdateEntity entity = new UpdateEntity ();
                   
                    string fileName = row[0].ToString();
                    entity.FileName = fileName;
                    entity.LastTime = (DateTime)row[1];
                    entity.Size = (long)row[2] ;
                    entity.IsUpdate = (bool)row[3];
                    entity.FileVersion = row[4].ToString();
                    entity.MD5 = row[5].ToString();
                    UpdateList.Add(entity);
                }
            }));
        }
        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="client"></param>
        private void DownFile(DataAccessCenterClient client)
        {
         
            Dictionary<UpdateEntity, byte[]> dic = new Dictionary<UpdateEntity, byte[]>();
            int count = UpdateList.Count;
            for (int i = 0; i < count; i++)
            {
                UpdateEntity entity = UpdateList[i];
                string fileName = entity.FileName;
                try
                {
                   
                    if (string.IsNullOrEmpty(_parmName))
                    {
                        entity.FileName = Environment.CurrentDirectory + "\\" + fileName;
                    }
                    else
                    {
                        entity.FileName = System.IO.Path.GetDirectoryName(_parmName) + "\\" + fileName;
                    }
                    string dirc = System.IO.Path.GetDirectoryName(entity.FileName);
                    if (dirc != string.Empty && !Directory.Exists(dirc))
                    {
                        Directory.CreateDirectory(dirc);
                    }
                    //文件如果存在
                    if (File.Exists(entity.FileName))
                    {
                        // 判断是否需要更新
                        if (!entity.IsUpdate)
                            continue;
                        //判断MD5是否相同，相同则不需要更新
                        if (GetMD5HashFromFile(entity.FileName) == entity.MD5)
                            continue;
                    }
                    if (entity.Size > 50 * 1024)
                    {
                        entity.UpdateState = "正在下载";
                        List<byte> listdata = new List<byte>();
                        long length = 0;
                        while (length != entity.Size)
                        {
                            object obj = client.GetObject(new ParamesEntity { ClassName = "DownLoadFile", MethodName = "DownLoadShare", Values = new object[] { fileName, length } });
                            if (obj != null)
                            {
                                byte[] data = (byte[])obj;
                                listdata.AddRange(data);
                                length += data.Length;
                            }
                        }
                        entity.UpdateState = "下载完成";
                        dic.Add(entity, listdata.ToArray());
                    }
                    else
                    {
                        byte[] data = (byte[])client.GetObject(new ParamesEntity { ClassName = "DownLoadFile", MethodName = "DownLoadFirst", Values = new object[] { fileName } });
                        entity.UpdateState = "下载完成";
                        dic.Add(entity, data);
                    }

                    SetProgress(i + 1, count);
                }
                catch (Exception ex)
                {
                    entity.UpdateState = "下载失败";
                }
            }

            SaveFile(dic);

            MessageBox.Show("更新成功");
            this.Dispatcher.Invoke(new Action(() =>
            {
                StartProcess();
                this.Close();
            }));
        }
             
    
        /// <summary>
        /// 保存文件
        /// </summary>
        private void SaveFile(Dictionary<UpdateEntity, byte[]> dic)
        {

            int count = dic.Count;
            if (count == 0)
            {
                throw new Exception("没有需要更新的文件");
            }

            int index = 0;
            foreach (UpdateEntity entity in dic.Keys)
            {
                try
                {
                    byte[] data = dic[entity];

                    using (FileStream stream = File.Open(entity.FileName, FileMode.Create, FileAccess.Write, FileShare.Write))
                    {
                        stream.Write(data, 0, data.Length);
                    }
                 
                    File.SetLastWriteTime(entity.FileName, entity.LastTime);
                    entity.UpdateState = "更新成功";

                    SetProgress(++index, count);
                }
                catch (Exception ex)
                {
                    entity.UpdateState = "更新失败";
                    MessageBox.Show(ex.Message);
                    SetProgress(++index, count);
                }
            }

        }

        private void SetProgress(int index,int count)
        {
            this.progressBar1.Dispatcher.Invoke(new Action(() =>
            {
                this.progressBar1.Value = index * 100 / count;
            }));
        }

        /// <summary>
        /// 获取文件MD5校验值
        /// </summary>
        /// <param name="fileName">文件路径</param>
        /// <returns></returns>
        public static string GetMD5HashFromFile(string fileName)
        {
            if (File.Exists(fileName))
            {
                byte[] retVal = null;
                using (FileStream file = File.OpenRead(fileName))
                {
                    System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                    retVal = md5.ComputeHash(file);
                }
                if (retVal != null)
                {
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < retVal.Length; i++)
                    {
                        sb.Append(retVal[i].ToString("x2"));
                    }
                    return sb.ToString().Trim().ToUpper();
                }
            }
            return string.Empty;
        }
    }

    public class UpdateEntity : BasePropertyChange
    {
        private string _filename = string.Empty;
        private DateTime _lasttime;
        private string _state = string.Empty;
        private long _size;
        private string _fileVersion =string.Empty;
        private string _md5 = string.Empty;
        public string FileName
        {
            get { return _filename; }
            set
            {
                if (value != _filename)
                {
                    _filename = value ;
                    this.NotifyPropertyChanged(o => o.FileName);
                }
            }
        }
        public DateTime LastTime
        {
            get { return _lasttime; }
            set
            {
                if (value != _lasttime)
                {
                    _lasttime = value;
                    this.NotifyPropertyChanged(o => o.LastTime);
                }
            }
        }
        public long Size
        {
            get { return _size; }
            set
            {
                if (value != _size)
                {
                    _size = value;
                    this.NotifyPropertyChanged(o => o.Size);
                }
            }
        }
        public string UpdateState
        {
            get { return _state; }
            set
            {
                if (value != _state)
                {
                    _state = value;
                    this.NotifyPropertyChanged(o => o.UpdateState);
                }
            }
        }
        public string FileVersion
        {
            get { return _fileVersion; }
            set
            {
                if (value != _fileVersion)
                {
                    _fileVersion = value;
                    this.NotifyPropertyChanged(o => o.FileVersion);
                }
            }
        }

        public bool IsUpdate { get; set; }
        public string MD5
        {
            get { return _md5; }
            set
            {
                if (value != _md5)
                {
                    _md5 = value;
                    this.NotifyPropertyChanged(o => o.MD5);
                }
            }
        }
    }


    [Serializable]
    public class BasePropertyChange : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged 成员
        [field: NonSerializedAttribute]
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        public void RegistProperty(string funcname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(funcname));
            }
        }
    }

    public static class PropertyChangedEx
    {
        public static void NotifyPropertyChanged<T, TProperty>(this T entity, Expression<Func<T, TProperty>> func) where T : BasePropertyChange
        {
            MemberExpression member = func.Body as MemberExpression;
            if (null != member)
            {
                entity.RegistProperty(member.Member.Name);
            }
            else
            {
                throw new Exception("表达式 必须是属性形式");
            }
        }
    }
}
